﻿using System;
using System.Collections.Generic;
using System.Linq;
using USU.MetricsMonitor.Common;
using USU.MetricsMonitor.LogProvider;
using USU.MetricsMonitor.LogParser;
using USU.MetricsMonitor.Storage;
using USU.MetricsMonitor.DiffProvider;
using USU.MetricsMonitor.Metrics;

namespace USU.MetricsMonitor.Updater
{
	public class Updater
	{
        private static readonly string usagePattern = "usage: {0} <projectName> <storagePath> <chunkLength> <whiteList> <blackList> [<repositaryUrl>]";
        private static string whiteList, blackList;
        private static StorageProvider storageProvider;

        private static void CalculateAndSave<T>(ProjectInfo proj, IEnumerable<RevisionInfo> parsedLog, BaseDiffProvider diffProvider, T m)
            where T : MetricCollection
        {
            m.Calculate(
                parsedLog, diffProvider, proj.Name, proj.WhiteList, proj.BlackList
            );
            storageProvider.SaveMetric(
                proj.Name, m
            );
        }

        public static void Main(string[] args)
        {
            try
            {
                //аргументы командной строки

                if (args.Length < 5 || args.Length > 6)
                    throw new ArgumentException(String.Format(usagePattern, Environment.GetCommandLineArgs()[0]));

                string
                    projectName = args[0],
                    storagePath = args[1],
                    repositaryUrl = (args.Length == 6 ? args[5] : null);

                uint chunkLength = uint.Parse(args[2]) + 1;

                whiteList = args[3];
                blackList = args[4];

                //необходимые переменные

                bool newproject;
                storageProvider = new StorageProvider(storagePath);

                Revision startRevision;

                LinesOfCode loc;
                PersonalRate pr;

                ProjectInfo project;
                List<ProjectInfo> projects = storageProvider.LoadProjects();

                if (repositaryUrl == null)
                {
                    //обновляем существующий

                    newproject = false;

                    project = projects.Find(p => { return p.Name == projectName; });

                    if (project == null)
                        throw new ArgumentException("проект с указанным именем не существует");

                    loc = storageProvider.LoadMetric<LinesOfCode>(projectName);
                    pr = storageProvider.LoadMetric<PersonalRate>(projectName);

                    startRevision = project.LastRevision;
                }
                else
                {
                    //скачиваем новый

                    newproject = true;

                    project = new ProjectInfo
                    {
                        Name = projectName,
                        Description = "",
                        RepositaryUrl = repositaryUrl,
                        WhiteList = whiteList,
                        BlackList = blackList
                    };
                    projects.Add(project);

                    loc = new LinesOfCode();
                    pr = new PersonalRate();

                    storageProvider.CreateProjectDirectory(projectName);

                    startRevision = new Revision(1);
                }

                string localStorage = storageProvider.PathToProjectDirectory(projectName);

                //загрузка данных и полсчет метрик

                BaseLogProvider logProvider = new SvnLogProvider(project.RepositaryUrl, localStorage);
                BaseDiffProvider diffProvider = new SvnDiffProvider(project.RepositaryUrl, localStorage);

                LogParserFromXml logParser = new LogParserFromXml();

                while (true)
                {
                    IEnumerable<RevisionInfo> parsedLog;

                    Console.Write("\r"+startRevision+" ревизия...");

                    using (var logStream = logProvider.GetLog(startRevision, chunkLength))
                        parsedLog = logParser.ParseLog(logStream);

                    if (newproject)
                    {
                        newproject = false;
                        project.BeginingDate = parsedLog.First().DateTime;
                    }
                    else
                        parsedLog = parsedLog.Skip(1);

                    if (parsedLog.Count() == 0) break;

                    CalculateAndSave<LinesOfCode>(project, parsedLog, diffProvider, loc);
                    CalculateAndSave<PersonalRate>(project, parsedLog, diffProvider, pr);

                    project.LastChangeDate = parsedLog.Last().DateTime;

                    startRevision = project.LastRevision = parsedLog.Last().Revision;

                    storageProvider.SaveProjects(projects);
                }
                Console.WriteLine("готово");
            }
            catch (Exception e)
            {
                string error = "Ошибка:", end = " "+e.Message;
                do
                    error += " > " + e.Message;
                while ((e = e.InnerException) != null);
                Console.Error.WriteLine(error + end);
            }
        }
	}
}